
open Ast
open Ast_fullfill
open Simplifier_remove_unused

let simplify ast =

	let rec simplify_program ast =
		simplify_body false ast
	
	and simplify_body in_function ast =
		let fullfill = fullfill_list fullfill_sourceElement {
			idents = StrSet.empty;
			labels = StrSet.empty;
			locals = StrSet.empty;
			} ast in

		let ast = Ast_tools.move_funcDecls_to_left ast in
		
		simplify_iteratively in_function fullfill.locals ast
	
	and simplify_iteratively in_function locals ast =
		let ast2 = ast in
		let locals, ast = simplify_locals in_function locals ast in
		let ast = List.fold_right simplify_sourceElement ast [] in
		if ast <> ast2 then simplify_iteratively in_function locals ast else ast
		
	and simplify_locals in_function locals ast =
		StrSet.fold (fun ident (locals,ast) -> 
			let var, ast2 = remove_unused_program {
				var_value = None;
				var_n_reads = 0;
				var_absolute = StrSet.empty;
				var_value_sure = true
			} {
				env_ident = ident;
				env_sure = true;
				env_in_function = in_function
			} ast in
				if var.var_n_reads < 2 then locals, ast2
				else (StrSet.add ident locals), ast) locals (StrSet.empty,ast)

	and simplify_sourceElement sourceElement sourceElements = match sourceElement with
		| AStatement stmt -> let stmts = simplify_statement stmt [] in
			(List.map (fun stmt -> AStatement stmt) stmts)@sourceElements
		(* | AFunctionDeclaration funcDecl -> simplify_functionDeclaration funcDecl *) (* Modif #1 *)

	and simplify_statement stmt stmts = match stmt with
		| ABlock block -> (simplify_block block)@stmts
		| AVariableStatement vs -> 
			let vs = simplify_variableStatement vs in
			(AVariableStatement vs)::stmts
		| AConstStatement cs -> 
			let cs = simplify_variableStatement cs in
			(AConstStatement cs)::stmts
		| AFuncDeclaration funcDecl -> 
			let funcDecl = simplify_functionDeclaration funcDecl in
			AFuncDeclaration(funcDecl)::stmts (* Modif #1 *)
		| AEmptyStatement -> stmts
		| AExpressionStatement expr -> 
			let expr = simplify_expression expr in
			if Simplifier_sideEffects.sideEffects_expression false expr 
			then (AExpressionStatement expr)::stmts
			else stmts
		| AIfStatement stmt -> 
			let stmt = simplify_ifStatement stmt in
			(AIfStatement stmt)::stmts
		| AIterationStatement stmt -> 
			let stmt = simplify_iterationStatement stmt in
			(AIterationStatement stmt)::stmts
		| AContinueStatement label -> (AContinueStatement label)::stmts
		| ABreakStatement label -> (ABreakStatement label)::stmts
		| AReturnStatement expr -> 
			let expr = simplify_optionalExpression expr in
			(AReturnStatement expr)::stmts
		| AWithStatement stmt -> 
			let stmt = simplify_withStatement stmt in
			(AWithStatement stmt)::stmts
		| ALabelledStatement (label,stmt) -> 
			(match simplify_statement stmt [] with
				| [] -> stmts
				(*| [stmt] -> stmt::stmts*)
				| lst -> let lst = (match (List.hd (List.rev lst)) with
						| ABreakStatement (Some j) when label = j -> (List.rev (List.tl (List.rev lst)))
						| _ -> lst) in
					if not (Simplifier_labels.is_label_used_in_statement label (ABlock lst)) 
						then lst@stmts else (ALabelledStatement (label,(ABlock lst)))::stmts
					
			)
		| ASwitchStatement stmt -> 
			let stmt = simplify_switchStatement stmt in
			(ASwitchStatement stmt)::stmts
		| AThrowStatement expr ->
			let expr = simplify_expression expr in 
			(AThrowStatement expr)::stmts
		| ATryStatement stmt -> 
			let stmt = simplify_tryStatement stmt in
			(ATryStatement stmt)::stmts
		| ADebuggerStatement -> ADebuggerStatement::stmts

	and simplify_block block =
		List.fold_right simplify_statement block []
		
	and simplify_variableStatement (is_private, is_meta, varDecls) = 
		is_private, is_meta, (List.map simplify_variableDeclaration varDecls)
	
	and simplify_variableDeclaration (ident,value) =
		ident, (match value with
			| None -> None
			| Some expr -> Some (simplify_assignmentExpression expr)
		)
	
	and simplify_functionDeclaration (is_private, is_meta, is_inline, ident, params, body) =
		is_private,is_meta,is_inline,ident,params,(simplify_body true body)
	
	and simplify_unique_statement stmt = 
		let stmts = simplify_statement stmt [] in
		(match stmts with
			| [] -> AEmptyStatement
			| [a] -> a
			| lst -> ABlock lst
		)
	
	and simplify_ifStatement = function
		| AIfElse (expr,stmt1,stmt2) -> 
			let expr = simplify_expression expr in
			let stmt1 = simplify_unique_statement stmt1 in
			let stmt1 = if Ast_then_ambiguous.then_ambiguous_statement stmt1 then (ABlock [stmt1]) else stmt1 in
			let stmt2 = simplify_unique_statement stmt2 in
			AIfElse (expr, stmt1, stmt2)
		| AIf (expr,stmt) -> 
			let expr = simplify_expression expr in
			let stmt = simplify_unique_statement stmt in
			AIf (expr, stmt)

	and simplify_iterationStatement = function
		| ADoWhile (stmt,expr) -> 
			let stmt = simplify_unique_statement stmt in
			let expr = simplify_expression expr in
			ADoWhile (stmt,expr)
		| AWhile (expr,stmt) -> 
			let expr = simplify_expression expr in
			let stmt = simplify_unique_statement stmt in
			AWhile (expr,stmt)
		| AFor (expr1, expr2, expr3, stmt) -> 
			let expr1 = simplify_optionalExpression expr1 in
			let expr2 = simplify_optionalExpression expr2 in
			let expr3 = simplify_optionalExpression expr3 in
			let stmt = simplify_unique_statement stmt in
			AFor (expr1, expr2, expr3, stmt)
		| AForVar (is_meta,varDecls, expr1, expr2, stmt) -> 
			let varDecls = List.map simplify_variableDeclaration varDecls in
			let expr1 = simplify_optionalExpression expr1 in
			let expr2 = simplify_optionalExpression expr2 in
			let stmt = simplify_unique_statement stmt in
			AForVar (is_meta,varDecls, expr1, expr2, stmt)
		| AForIn (lhs, expr, stmt) -> 
			let lhs = simplify_leftHandSideExpression lhs in
			let expr = simplify_expression expr in
			let stmt = simplify_unique_statement stmt in
			AForIn (lhs, expr, stmt)
		| AForVarIn (is_meta,varDecl, expr, stmt) -> 
			let varDecl = simplify_variableDeclaration varDecl in
			let expr = simplify_expression expr in
			let stmt = simplify_unique_statement stmt in
			AForVarIn (is_meta,varDecl, expr, stmt)
	
	and simplify_optionalExpression = function
		| None -> None
		| Some expr -> Some (simplify_expression expr)

	and simplify_withStatement (expr,stmt) = 
		expr, (simplify_unique_statement stmt)
	
	and simplify_switchStatement (expr,caseBlock) =
		let expr = simplify_expression expr in
		let caseBlock = simplify_caseBlock caseBlock in
		expr, caseBlock
	
	and simplify_caseBlock = function
		| ACasesNoDefault caseClauses -> 
			let caseClauses = List.map simplify_caseClause caseClauses in
			ACasesNoDefault caseClauses
		| ACasesDefault (caseClauses1, defaultClause, caseClauses2) -> 
			let caseClauses1 = List.map simplify_caseClause caseClauses1 in
			let defaultClause = simplify_block defaultClause in
			let caseClauses2 = List.map simplify_caseClause caseClauses2 in
			ACasesDefault (caseClauses1, defaultClause, caseClauses2 )

	and simplify_caseClause (expr,stmts) = 
		(simplify_expression expr),(simplify_block stmts)
	
	and simplify_tryStatement = function
		| ATryCatch (block,catch) -> 
			let block = simplify_block block in
			let catch = simplify_catch catch in
			ATryCatch (block, catch)
		| ATryFinally (block,finally) -> 
			let block = simplify_block block in
			let finally = simplify_block finally in
			ATryFinally (block, finally)
		| ATryCatchFinally (block, catch, finally) -> 
			let block = simplify_block block in
			let catch = simplify_catch catch in
			let finally = simplify_block finally in
			ATryCatchFinally (block, catch, finally)
	
	and simplify_catch (ident,block) =
		ident, (simplify_block block)
	
	and simplify_expression expr =
		List.map simplify_assignmentExpression expr
	
	and simplify_assignmentExpression = function
		| ACondExpr (ACondBinaryExpr(AUnaryExpr(APostfixExpr(ALeftHandSide(ANewExpression(AMemberExpression(APrimaryExpr(AExpression([a]))))))))) ->
			simplify_assignmentExpression a
		| ACondExpr expr -> ACondExpr (simplify_conditionalExpression expr)
		| AAssignOp (lhs,op,expr) ->
			let lhs = simplify_leftHandSideExpression lhs in
			let expr = simplify_assignmentExpression expr in
			AAssignOp (lhs, op, expr)
		
	and simplify_conditionalExpression = function
		| ACondBinaryExpr expr -> ACondBinaryExpr (simplify_binaryExpression expr)
		| ACondTernaryExpr (expr1, expr2, expr3) -> 
			let expr1 = simplify_binaryExpression expr1 in
			let expr2 = simplify_assignmentExpression expr2 in
			let expr3 = simplify_assignmentExpression expr3 in
			ACondTernaryExpr (expr1, expr2, expr3)
	
	and simplify_binaryExpression = function
		| AUnaryExpr expr -> AUnaryExpr (simplify_unaryExpression expr)
		| ABinaryExpr (op, expr1, expr2) -> 
			let expr1 = simplify_binaryExpression expr1 in
			let expr2 = simplify_binaryExpression expr2 in
			ABinaryExpr (op, expr1, expr2)
	
	and simplify_unaryExpression = function
		| APostfixExpr expr -> APostfixExpr (simplify_postfixExpression expr)
		| AOpUnaryExpr (op, expr) -> AOpUnaryExpr (op, simplify_unaryExpression expr)

	and simplify_postfixExpression = function
		| ALeftHandSide lhs -> ALeftHandSide (simplify_leftHandSideExpression lhs)
		| ALeftHandSideOp (lhs,op) -> ALeftHandSideOp (lhs,op)

	and simplify_leftHandSideExpression = function
		| ANewExpression expr -> ANewExpression (simplify_newExpression expr)
		| ACallExpression expr -> ACallExpression (simplify_callExpression expr)
	
	and simplify_callExpression = function
		| ACallMemberArgs (expr, args) -> 
			let expr = simplify_memberExpression expr in
			let expr = (match expr with
				| AFunctionExpr _ -> APrimaryExpr (AExpression ([ACondExpr (ACondBinaryExpr(AUnaryExpr(APostfixExpr(ALeftHandSide(ANewExpression(AMemberExpression(expr)))))))]) )
				| _ ->  expr
			) in
			let args = simplify_arguments args in
			ACallMemberArgs (expr, args)
		| ACallExpressionArgs (expr, args) ->
			let expr = simplify_callExpression expr in
			let args = simplify_arguments args in
			ACallExpressionArgs (expr, args)
		| ACallExpressionExpr (expr1, expr2) ->
			let expr1 = simplify_callExpression expr1 in
			let expr2 = simplify_expression expr2 in
			ACallExpressionExpr (expr1, expr2)
		| ACallExpressionIdent (expr, ident) -> 
			let expr = simplify_callExpression expr in
			ACallExpressionIdent (expr, ident)
	
	and simplify_newExpression = function
		| AMemberExpression(APrimaryExpr(AExpression([
					ACondExpr(ACondBinaryExpr(AUnaryExpr(APostfixExpr(ALeftHandSide(ANewExpression(expr))))))
				]))) -> simplify_newExpression expr
		| AMemberExpression expr -> AMemberExpression (simplify_memberExpression expr)
		| ANewMemberExpression expr -> ANewMemberExpression (simplify_newExpression expr)
	
	and simplify_memberExpression = function
		| APrimaryExpr (AExpression ([ACondExpr (ACondBinaryExpr(AUnaryExpr(APostfixExpr(ALeftHandSide(ANewExpression(AMemberExpression(expr)))))))]) ) ->
			simplify_memberExpression expr
		| APrimaryExpr expr -> APrimaryExpr (simplify_primaryExpression expr)
		| AFunctionExpr expr -> AFunctionExpr (simplify_functionExpression expr)
		| AMemberBracketExpr (expr1, expr2) -> 
			let expr1 = simplify_memberExpression expr1 in
			let expr2 = simplify_expression expr2 in
			let expr1 = (match expr1 with
				| AFunctionExpr _ -> APrimaryExpr (AExpression ([ACondExpr (ACondBinaryExpr(AUnaryExpr(APostfixExpr(ALeftHandSide(ANewExpression(AMemberExpression(expr1)))))))]) )
				| _ ->  expr1
			) in
			AMemberBracketExpr (expr1, expr2)
		| AMemberDotExpr (expr, ident) -> 
			let expr = simplify_memberExpression expr in
			let expr = (match expr with
				| AFunctionExpr _ -> APrimaryExpr (AExpression ([ACondExpr (ACondBinaryExpr(AUnaryExpr(APostfixExpr(ALeftHandSide(ANewExpression(AMemberExpression(expr)))))))]) )
				| _ ->  expr
			) in
			AMemberDotExpr (expr, ident)
		| AMemberNewExpr (expr, args) ->
			let expr = simplify_memberExpression expr in
			let args = simplify_arguments args in
			AMemberNewExpr (expr, args)
			
	and simplify_arguments args =
		List.map simplify_assignmentExpression args
		
	and simplify_functionExpression (is_inline, ident, params, body) = 
		is_inline, ident, params, (simplify_program body)
	
	and simplify_primaryExpression = function
		| AThis -> AThis
		| AIdentifier ident -> AIdentifier ident
		| ALiteral literal -> ALiteral literal
		| AArrayLiteral literal -> AArrayLiteral (simplify_arrayLiteral literal)
		| AObjectLiteral literal -> AObjectLiteral (simplify_objectLiteral literal)
		| AExpression expr -> AExpression (simplify_expression expr)
	
	and simplify_arrayLiteral = function
		| AElisions elisions -> AElisions elisions
		| AElements elements -> AElements (List.map simplify_element elements)
		| AElementsAndElisions (elements,elisions) -> 
			let elements = List.map simplify_element elements in
			AElementsAndElisions (elements, elisions)
	
	and simplify_element (elisions, expr) =
		elisions, (simplify_assignmentExpression expr)
	
	and simplify_objectLiteral props =
		List.map simplify_propertyAssignment props
	
	and simplify_propertyAssignment = function
		| APropAssignment (prop, expr) ->
			let expr = simplify_assignmentExpression expr in
			APropAssignment (prop, expr)
		| APropGet (prop, body) -> 
			let body = simplify_program body in
			APropGet (prop, body)
		| APropSet (prop, ident, body) ->
			let body = simplify_program body in
			APropSet (prop, ident, body)
		
	in
	simplify_program ast

